package com.ybkj.daijia.server.zhuan;

import com.ybkj.daijia.server.model.Area;
import com.ybkj.daijia.server.model.ChargeStartDistance;
import com.ybkj.daijia.server.model.ChargeStartTime;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

/**
 * 计价器 为每一个司机添加一个计价器 负责计算一次旅行需要支付的总价, 实际乘客或司机支付的价格会以此为依据, 但不一定相等
 * @author zhyu
 * @date 2019-08-17 22:55:03
 * @lifecycle 初始化: 销毁: 使用:订单跟踪, 调试价格, 司机端传参计算价格 更新:
 * 
 */
@Data
@Slf4j
public class SelectPrice implements Serializable {


    private static final long serialVersionUID = 1L;

    /**
     * 计算价格的时间
     */
    private Long computeTime;
    /**
     * 收费参数
     *
     * @see Order#areaId
     */
    private Long areaId;

    /**
     * 司机的最后位置
     */
    private SmallLocation lastLoc;

    /**
     * 最小更新时间间隔
     */
    private static final Long MIN_FRESH_PRICE_TIME_STEP = 30 * 1000L;

    /***
     * 远程费用
     */
    private BigDecimal remoteCost;

    /**
     * 选择计价规则依据的时间, 快车的是下单时间
     */
    private Long priceTime;

    /**
     * 距离的更新日期
     */
    private Long distanceDataDate;

    /**
     * 等待的分钟数
     */
    private Long waitSeconds;
    /**
     * 开始等待时间
     *
     * @see Order#startWait
     */
    private Long startWait;
    /**
     * 开始出发时间
     *
     * @sql select created from zhuanche.zc_order where orderId = ? and status = {@link Order#ORDER_STATUS_ZHIXING}
     */
    private Long startRun;

    /**
     * 到达目的地时间
     *
     * @sql select created from zhuanche.zc_order where orderId = ? and status = {@link Order#ORDER_STATUS_ARRIVE_DISTANCE}
     */
    private Long endRun;

    /**
     * 已经旅程公里数 最后会放入{@link Order#mileage}
     *
     */
    private Double runKilometers;

    /**
     * 所有价格规则
     */
    transient protected List<ChargeStartTime> prices;

    /**
     * 符合条件的收费时段(最终使用的)
     */
    transient protected ChargeStartTime price;

    /**
     * 起步费用
     */
    public BigDecimal qbCost;

    /**
     * 等候费用
     */
    public BigDecimal waitCost;

    /**
     * 行驶时间费用
     */
    public BigDecimal travalTimeCost;

    /**
     * 里程费
     */
    public BigDecimal disCost;

    /*
     * 旅行时间
     */
    public Long travelSeconds;

    /**
     * 旅程总价格
     */
    public BigDecimal shouldCash;

    public Double getRunKilometers() {
        return null == runKilometers ? 0 : runKilometers;
    }

    public void setRunKilometers(Double runKilometers) {
        this.runKilometers = runKilometers;
        distanceDataDate = System.currentTimeMillis();
    }

    public SelectPrice() {
        returnPriceToZero();
    }

    /**
     * 根据付费信息和付费选择时间初始化计价器
     *
     * @param area
     * @param priceTime 赛选计价时间段的时间点, 允许为null
     */
    public void initByArea(Area area, Date priceTime) {
        prices = area.getChargeStartTimes();
        price = selectPrice(priceTime);
        areaId = area.getId();
    }


    /**
     * 除了计价数据外,全初始化, 再次计算价格
     *
     * @param orderStatus
     * @param runTime
     * @param mileage
     * @param waitSeconds
     */
    public void reComputePrice(int orderStatus, Long runTime, Double mileage, Long waitSeconds) {
        returnPriceToZero();
        caculatePrice(orderStatus, runTime, mileage, waitSeconds);
    }

    /***
     * 利用先有的数据更新价格
     * 异常返回false
     */
    public boolean freshPrice(int orderStatus) {
        return freshPrice(orderStatus, false);
    }

    /***
     * 利用先有的数据更新价格
     * @param must 是否强制更新
     *  没有到更新时刻或者成功更新返回true, 数据不对返回false
     */
    public boolean freshPrice(int orderStatus, boolean must) {
        boolean ok = true;
        //已接单
        if (orderStatus >= 2 && null == price) {
            ok = false;
        }
        if (orderStatus >= 3 && null == startWait) {
            ok = false;
        }
        if (orderStatus >= 4 && null == startRun) {
            ok = false;
        }
        if (orderStatus >= 5 && null == endRun) {
            ok = false;
        }
        if (!ok) {
            return ok;
        }
        if (must || null == computeTime || System.currentTimeMillis() - computeTime > MIN_FRESH_PRICE_TIME_STEP) {
            if (null == startWait) {

            } else if (null == startRun) {
                waitSeconds = (System.currentTimeMillis() - startWait) / 1000;
            } else if (null == endRun) {
                waitSeconds = (startRun - startWait) / 1000;
                travelSeconds = (System.currentTimeMillis() - startRun) / 1000;
            } else if (null != endRun) {
                waitSeconds = (startRun - startWait) / 1000;
                travelSeconds = (endRun - startRun) / 1000;
            }
            caculatePrice(orderStatus);
        }
        return true;
    }

    /**
     * 归零价格
     */
    private void returnPriceToZero() {
        shouldCash = BigDecimal.ZERO;
        remoteCost = BigDecimal.ZERO;
        disCost = BigDecimal.ZERO;
        travalTimeCost = BigDecimal.ZERO;
        qbCost = BigDecimal.ZERO;
        waitCost = BigDecimal.ZERO;
    }

    /**
     * 归零价格,根据参数计算价格
     * @param orderStatus
     */
    private void caculatePrice(int orderStatus) {
        returnPriceToZero();
        //获取起步价格
        qbCost = qbFee();
        //计算里程费
        disCost = calcuteDrive(runKilometers);
        travalTimeCost = calcuteJsTimeCost(travelSeconds);
        remoteCost = calculateRemote(runKilometers);
        waitCost = calculateWait(waitSeconds);
        shouldCash = qbCost.add(disCost).add(travalTimeCost).add(waitCost).add(remoteCost);
        computeTime = System.currentTimeMillis();
        log.info("compute price, runKilometers = {}, travelSeconds = {}, waitSeconds = {},qbCost = {}, remoteCost = {}, " +
                "waitCost = {},travalTimeCost = {}, shouldCash = {}, computeTime = {}, priceId = {}, orderStatus = {}", runKilometers, travelSeconds, waitSeconds, qbCost, remoteCost,
            waitCost, travalTimeCost, shouldCash, computeTime, price.getId(), orderStatus);
    }

    /**
     * 传入计价参数, 计算价格
     * @param orderStatus
     * @param runTime
     * @param mileage
     * @param waitSeconds
     */
    private void caculatePrice(int orderStatus, Long runTime, Double mileage, Long waitSeconds) {
        this.travelSeconds = runTime;
        this.runKilometers = mileage;
        this.waitSeconds = waitSeconds;
        caculatePrice(orderStatus);
    }

    /**
     * 重新确定计价规则, 根据计价参数计算价格
     * @param runTime
     * @param waitSeconds
     * @param mileage
     * @param area
     * @param dateTime
     */
    public void computePrice(int orderStatus, Long runTime, Long waitSeconds, Double mileage, Area area, Date dateTime) {
        initByArea(area, dateTime);
        caculatePrice(orderStatus, runTime, mileage, waitSeconds);
    }

    /**
     * 计算时间价
     * (m - m0 * 60)/60/u *p
     * @param travelSeconds
     * @return
     */
    public BigDecimal calcuteJsTimeCost(Long travelSeconds) {
        if (null == price || null == travelSeconds || 0 == travelSeconds) {
            return BigDecimal.ZERO;
        }
        //获取免费时长
        double qbsj = price.getQbsj();
        //获取计算单元
        double djsjUnit = price.getDjsjUnit();
        BigDecimal djsjCost = price.getDjsjCost();
        if (travelSeconds <= qbsj) {
            //免费时长内不要钱
            return BigDecimal.ZERO;
        } else {
            if (djsjUnit > 0) {
                //先以秒 计算需要计价的秒数
                double extraTime = travelSeconds - (long) qbsj * 60;
                if (extraTime > 0) {
                    //再根据分钟数 计算单元次数(不舍入)
                    double unitnum = extraTime / (djsjUnit * 60);
                    //单元次数 * 单元价格
                    return new BigDecimal(unitnum).multiply(djsjCost).setScale(2, BigDecimal.ROUND_HALF_UP);
                }
                return BigDecimal.ZERO;
            } else {
                return BigDecimal.ZERO;
            }
        }
    }


    /**
     * 计算里程价
     * 公式  (s -s0)/u*p
     * @param distance
     * @return
     */
    public BigDecimal calcuteDrive(Double distance) {
        if (null == price || null == distance || 0 == distance) {
            return BigDecimal.ZERO;
        }
        //公里
        double qblc = price.getQblc();
        if (distance <= qblc) {
            return BigDecimal.ZERO;
        }
        //多出来的里程数
        double difflc = distance - qblc;
        //里程的计算单元,一般是1km
        double djglUnit = price.getDjglUnit();
        // 计费次数直接用double计算(不舍也不入);
        double countTimes = (difflc / djglUnit);
        // 每次循环的金额
        BigDecimal djglCost = price.getDjglCost();
        //最后结果保留两位小数
        return new BigDecimal(countTimes).multiply(djglCost).setScale(2, BigDecimal.ROUND_HALF_UP);

    }

    /***
     * 计算远程价格
     * @param distance
     * @return
     * 区间计价
     * 每个区间都要计算里程在该区间的里程和价格
     */
    public BigDecimal calculateRemote(Double distance) {
        if (price == null || null == distance || distance == 0) {
            return BigDecimal.ZERO;
        }
        List<ChargeStartDistance> distances = price.getChargeStartDistances();
        if (!CollectionUtils.isEmpty(distances)) {
            remoteCost = null == remoteCost ? BigDecimal.ZERO : remoteCost;
            for (ChargeStartDistance distancePriceInfo : distances) {
                double num = distance - distancePriceInfo.getStartdistance();
                if (num > 0) {
                    double max = distancePriceInfo.getDistance() - distancePriceInfo.getStartdistance();
                    if (max < num) {
                        num = max;
                    }
                    remoteCost = remoteCost.add(BigDecimal.valueOf(num).multiply(distancePriceInfo.getMoney())).setScale(2, BigDecimal.ROUND_HALF_UP);
                }
            }
        }
        return remoteCost;
    }

    /**
     * 计算等待价
     * @param waitSeconds
     * @return 公式 (w - w0 * 60) /60 /u * p
     */
    public BigDecimal calculateWait(Long waitSeconds) {
        if (price == null || null == waitSeconds || 0 == waitSeconds) {
            return BigDecimal.ZERO;
        }
        Integer mfdhsj = price.getMfdhsj();
        double sub = waitSeconds - mfdhsj * 60;
        if (sub <= 0) {
            return BigDecimal.ZERO;
        }
        Integer dhsjUnit = price.getDhsjUnit();
        BigDecimal dhsjCost = price.getDhsjCost();
        //  (w - w0 * 60) /60 /u * p
        double unit = sub / dhsjUnit;
        return BigDecimal.valueOf(unit).divide(BigDecimal.valueOf(60), BigDecimal.ROUND_HALF_UP).multiply(dhsjCost).setScale(2, BigDecimal.ROUND_HALF_UP);
    }

    public static void main(String[] args) {
        BigDecimal travalTimeCost = null;
        int qbsj = 8;
        int djsjUnit = 1;
        double djsjCost = 0.16;
        long travelSeconds = 1000;
        if (travelSeconds <= qbsj * 60) {
            travalTimeCost = BigDecimal.ZERO;
        } else {
            if (djsjUnit > 0) {
                double extraTime = travelSeconds - (long) qbsj * 60;
                double unitnum = extraTime / (djsjUnit * 60);
                travalTimeCost = new BigDecimal(unitnum).multiply(BigDecimal.valueOf(djsjCost)).setScale(2, BigDecimal.ROUND_HALF_UP);
            } else {
                travalTimeCost = BigDecimal.ZERO;
            }
        }
        System.out.println(travalTimeCost);
    }

    /**
     * 根据时间获取计价规则
     * @param dateTime
     * @return
     */
    protected ChargeStartTime selectPrice(Date dateTime) {
        // 起步时间
        Calendar calendar = Calendar.getInstance(Locale.CHINESE);
        if (null == dateTime) {
            Date outsetTime = new Date(System.currentTimeMillis());
            calendar.setTime(outsetTime);
        } else {
            calendar.setTime(dateTime);
        }
        this.priceTime = calendar.getTime().getTime();
        // 小时
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        // 分钟
        int minute = calendar.get(Calendar.MINUTE);
        int hourminute = (hour * 60) + minute;
        for (ChargeStartTime chargeTime : prices) {
            if (!chargeTime.isEmpty()) {
                if (chargeTime.getStartHour() <= chargeTime.getEndHour()) {
                    if (hourminute >= (chargeTime.getStartHour() * 60) + chargeTime.getStartMinute()
                        && hourminute <= (chargeTime.getEndHour() * 60) + chargeTime.getEndMinute()) {
                        price = chargeTime;
                        return chargeTime;
                    }
                } else {
                    int start1 = (chargeTime.getStartHour() * 60) + chargeTime.getStartMinute();
                    int end1 = 24 * 60;
                    int start2 = 0;
                    int end2 = (chargeTime.getEndHour() * 60) + chargeTime.getEndMinute();
                    boolean b1 = hourminute >= start1 && hourminute <= end1;
                    boolean b2 = hourminute >= start2 && hourminute <= end2;
                    if (b1|| b2) {
                        price = chargeTime;
                        return chargeTime;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 获取起步价
     * @return
     */
    protected BigDecimal qbFee() {
        if (null == prices || prices.size() == 0) {
            return BigDecimal.ZERO;
        }
        return price.getCost();
    }
}